﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using TemplateEngineWithChart.Docx.Errors;
using TemplateEngineWithChart.Docx.Processors.ProcessResults;

namespace TemplateEngineWithChart.Docx.Processors
{
	internal class RepeatProcessor : IProcessor
	{
        #region fields

        private bool _isNeedToRemoveContentControls;
		private readonly ProcessContext _context;

        #endregion fields

        #region constructors

        public RepeatProcessor(ProcessContext context)
        {
            _context = context;
        }

        #endregion constructors

        #region methods

        private PropagationProcessResult PropagatePrototype(Prototype prototype, IEnumerable<ListItemContent> content)
        {
            var processResult = new PropagationProcessResult();
            var newRows = new List<XElement>();

            foreach (var contentItem in content)
            {
                // Create new item from the prototype.
                var newItemEntry = prototype.Clone();

                foreach (var xElement in newItemEntry.PrototypeItems)
                {
                    var newElement = new XElement(xElement);
                    if (!newElement.DescendantsAndSelf(W.sdt).Any())
                    {
                        newRows.Add(newElement);
                        continue;
                    }

                    foreach (var sdt in newElement.FirstLevelDescendantsAndSelf(W.sdt).ToList())
                    {
                        var fieldContent = contentItem.GetContentItem(sdt.SdtTagName());
                        if (fieldContent == null)
                        {
                            processResult.AddError(new CustomError(
                                string.Format("Field content for field '{0}' not found",
                                sdt.SdtTagName())));

                            continue;
                        }

                        var contentProcessResult = new ContentProcessor(_context)
                            .SetRemoveContentControls(_isNeedToRemoveContentControls)
                            .FillContent(sdt, fieldContent);

                        processResult.Merge(contentProcessResult);
                    }
                    newRows.Add(newElement);
                }
            }
            processResult.Result = newRows;
            return processResult;
        }

        private ProcessResult FillContent(XElement contentControl, IContentItem item)
        {
            var processResult = ProcessResult.NotHandledResult;
            if (!(item is RepeatContent))
            {
                return ProcessResult.NotHandledResult;
            }

            var repeat = item as RepeatContent;

            if (contentControl != null)
            {
                var itemsContentControl = contentControl
                    .Descendants(W.sdt)
                    .FirstOrDefault();

                if (itemsContentControl != null)
                {
                    if (repeat.IsHidden || repeat.Items == null)
                    {
                        contentControl.DescendantsAndSelf(W.sdt).Remove();
                    }
                    else
                    {
                        var fieldNames = repeat.FieldNames.ToList();

                        // Create a prototype of new items to be inserted into the document.
                        var prototype = new Prototype(_context, contentControl, fieldNames);

                        if (prototype.IsValid)
                        {
                            // Propagates a prototype.
                            var propagationResult = PropagatePrototype(prototype, repeat.Items);

                            processResult.Merge(propagationResult);

                            // Remove the prototype row and add all of the newly constructed rows.
                            prototype.PrototypeItems.Last().AddAfterSelf(propagationResult.Result);
                            prototype.PrototypeItems.Remove();
                        }
                    }
                }
            }    

            processResult.AddItemToHandled(repeat);

            return processResult;
        }

        public IProcessor SetRemoveContentControls(bool isNeedToRemove)
        {
            _isNeedToRemoveContentControls = isNeedToRemove;
            return this;
        }
        public ProcessResult FillContent(XElement contentControl, IEnumerable<IContentItem> items)
        {
            var processResult = ProcessResult.NotHandledResult;
            var handled = false;

            foreach (var contentItem in items)
            {
                var itemProcessResult = FillContent(contentControl, contentItem);
                processResult.Merge(itemProcessResult);

                if (!itemProcessResult.Handled) continue;

                handled = true;
            }

            if (!handled) return processResult;

            if (processResult.Success && _isNeedToRemoveContentControls)
            {
                foreach (var sdt in contentControl.Descendants(W.sdt).ToList())
                {
                    // Remove the content control, and replace it with its contents.
                    sdt.RemoveContentControl();
                }
                contentControl.RemoveContentControl();
            }
            return processResult;
        }

        #endregion methods
	}
}
